﻿/*
   Copyright 2013 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Jurassic.Library;
using Flower.Services;
using Jurassic;
using Flower.Directory.Scripting;
using Flower.Directory.Util;
using Flower.Services.Data;

namespace Flower.Directory.Management.Scripting
{
    public class QueueObject : ObjectInstance
    {
        private readonly IQueueService _queueService;

        public QueueObject(ObjectInstance prototype, IQueueService queueService)
            : base(prototype)
        {
            _queueService = queueService;

            PopulateFunctions();
        }

        [JSFunction(Name = "getRecipients")]
        public ArrayInstance GetRecipients(int start, int limit)
        {
            try
            {
                ArrayInstance result = Engine.Array.New();

                IList<string> recipients =
                    _queueService.GetRecipients(start, limit);

                foreach (string rcp in recipients)
                {
                    result.Push(rcp);
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message);
            }
        }

        private ObjectInstance MapQueueServiceMessasge(QueueServiceMessage message)
        {
            ObjectInstance result = Engine.Object.Construct();

            result.SetPropertyValue("id", message.Id.ToString(), true);
            result.SetPropertyValue("recipient", message.Recipient, true);
            result.SetPropertyValue("putOn", message.PutOn, true);
            result.SetPropertyValue("data", new XmlObject(Engine.Object.InstancePrototype, message.Data), true);

            return result;
        }

        [JSFunction(Name = "getMessagesById")]
        public ArrayInstance GetMessagesById(ArrayInstance ids)
        {
            try
            {
                var guids = new List<Guid>();

                foreach (object el in ids.ElementValues)
                {
                    guids.Add(Guid.Parse(el.ToString()));
                }

                IList<QueueServiceMessage> messages =
                    _queueService.GetMessagesById(guids);

                ArrayInstance result = Engine.Array.New();

                foreach (QueueServiceMessage msg in messages)
                {
                    result.Push((object)MapQueueServiceMessasge(msg));
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message);
            }
        }

        [JSFunction(Name = "getMessagesByRecipient")]
        public ArrayInstance GetMessagesByRecipient(ArrayInstance recipients, object start, object end)
        {
            try
            {
                DateTime? startDate = null, endDate = null;
                DateInstance dateInstance;

                if (start != null)
                {
                    if (start is DateTime)
                    {
                        startDate = (DateTime)start;
                    }
                    else if ((dateInstance = start as DateInstance) != null) 
                    {
                        startDate = dateInstance.Value;
                    }
                }

                if (end != null)
                {
                    if (end is DateTime)
                    {
                        endDate = (DateTime)end;
                    }
                    else if ((dateInstance = end as DateInstance) != null)
                    {
                        endDate = dateInstance.Value;
                    }
                }

                var rcps = new List<string>();

                foreach (object rcp in recipients.ElementValues)
                {
                    rcps.Add(rcp.ToString());
                }

                IList<QueueServiceMessage> messages =
                    _queueService.GetMessagesByRecipient(rcps, startDate, endDate);

                ArrayInstance result = Engine.Array.New();

                foreach (QueueServiceMessage msg in messages)
                {
                    result.Push((object)MapQueueServiceMessasge(msg));
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message);
            }
        }

        [JSFunction(Name = "removeMessages")]
        public void RemoveMessages(ArrayInstance ids)
        {
            try
            {
                var guids = new List<Guid>();

                foreach (object el in ids.ElementValues)
                {
                    guids.Add(Guid.Parse(el.ToString()));
                }

                _queueService.RemoveMessages(guids);
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message);
            }
        }

        [JSFunction(Name = "removeRecipients")]
        public void RemoveRecipients(ArrayInstance recipients)
        {
            try
            {
                var rcps = new List<string>();

                foreach (object rcp in recipients.ElementValues)
                {
                    rcps.Add(rcp.ToString());
                }

                _queueService.RemoveRecipients(rcps);
            }
            catch (Exception ex)
            {
                throw new JavaScriptException(Engine, "Error", ex.Message);
            }
        }
    }

    public class QueueObjectConstructor : ClrFunction
    {
        public QueueObjectConstructor(ScriptEngine engine)
            : base(
                engine.Function.InstancePrototype,
                "queue",
                engine.Object.InstancePrototype
            )
        {
        }

        [JSCallFunction]
        public QueueObject Create(string queuePath)
        {
            var dir = Engine.GetGlobalValue("$dir") as DirectoryObject;

            if (dir == null)
            {
                throw new JavaScriptException(Engine, "Error", "The directory is not initialized");
            }

            try
            {
                string path, id;

                DirectoryUtil.CutPathTailSegment(queuePath, out path, out id);

                var ent = dir.Directory.Get(path);

                if (ent == null)
                {
                    throw new JavaScriptException(Engine, "Error", "The entry not found");
                }

                var client = DirectoryUtil.GetService<IQueueService>(ent, id);

                if (client == null)
                {
                    throw new JavaScriptException(Engine, "Error", "The queue doesn't exist");
                }

                return new QueueObject(Engine.Object.InstancePrototype, client);
            }
            catch (Exception ex)
            {
                if (ex is JavaScriptException)
                {
                    throw;
                }

                throw new JavaScriptException(Engine, "Error", string.Format("Failed to create an instance of a queue: {0}", ex.Message), ex);
            }
        }
    }
}
